/*
 * ============================================================================
 *                   GNU Lesser General Public License
 * ============================================================================
 *
 *
 *
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
 * 
 *
 *
 */
package rad.framework.trace;

import java.util.Arrays;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

import rad.framework.lang.ExceptionUtil;
import rad.framework.validator.ValidationUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.validator.InvalidValue;

/**
 * @author boudyacho
 */
public class TraceInterceptor {

	/** logger for the ctx bean */
	private transient Log logger;

	/**
	 * @param ctx
	 * @return
	 * @throws Exception
	 */
	@AroundInvoke
	public Object aroundInvoke(InvocationContext ctx) throws Exception {
		logger = LogFactory.getLog(ctx.getTarget().getClass());
		try {
			if (logger.isDebugEnabled()) {
				pre(ctx);
				Object output = ctx.proceed();
				post(ctx, output);
				return output;
			} else {
				return ctx.proceed();
			}
		} catch (Throwable e) {
			Throwable cause = ExceptionUtil.getRootCause(e);
			if (logger.isWarnEnabled()) {
				InvalidValue[] errors = ValidationUtil.getInvalidValues(cause);
				if (errors.length > 0) {
					logger.warn("Validation Errors: "
						+ Arrays.toString(errors));
				}
				logger.warn("Throwing: ", cause);
			}
			if (e instanceof Exception) {
				throw (Exception) e;
			} else {
				throw new Exception(e);
			}
		}
	}

	@PostConstruct
	public void postConstruct(InvocationContext ctx) {
		logger = LogFactory.getLog(ctx.getTarget().getClass());
		if (logger.isDebugEnabled()) {
			logger.debug("postConstruct: " + ctx.getTarget());
		}
	}

	@PreDestroy
	public void preDestroy(InvocationContext ctx) {
		logger = LogFactory.getLog(ctx.getTarget().getClass());
		if (logger.isDebugEnabled()) {
			logger.debug("preDestroy: " + ctx.getTarget());
		}
	}

	@PrePassivate
	public void prePassivate(InvocationContext ctx) {
		logger = LogFactory.getLog(ctx.getTarget().getClass());
		if (logger.isDebugEnabled()) {
			logger.debug("prePassivate: " + ctx.getTarget());
		}
	}

	@PostActivate
	public void postActivate(InvocationContext ctx) {
		logger = LogFactory.getLog(ctx.getTarget().getClass());
		if (logger.isDebugEnabled()) {
			logger.debug("postActivate: " + ctx.getTarget());
		}
	}

	/**
	 * @param ctx
	 * @return
	 */
	private void createTraceName(StringBuffer buffer, InvocationContext ctx) {
		buffer.append(ctx.getTarget().getClass().getName());
		buffer.append(".");
		buffer.append(ctx.getMethod().getName());
	}

	/**
	 * Log arguments
	 * 
	 * @param invocation
	 */
	private void pre(InvocationContext ctx) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("Invoking: ");
		createTraceName(buffer, ctx);
		buffer.append("\n");
		Object[] args = ctx.getParameters();
		if (args != null) {
			buffer.append("Parameters:\n");
			for (int i = 0; i < args.length; i++) {
				if (args[i] != null) {
					buffer.append(args[i].toString());
					buffer.append("\n");
				}
			}
		}
		logger.debug(buffer.toString());
	}

	/**
	 * Log output
	 * 
	 * @param output
	 */
	private void post(InvocationContext ctx, Object output) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("Returning: ");
		createTraceName(buffer, ctx);
		buffer.append("\n");
		if (output != null) {
			buffer.append(output.toString());
		}
		logger.debug(buffer.toString());
	}
}